﻿//---------------------------------------------------------------------------
// <copyright file="RibbonTitlePanel.cs" company="Microsoft Corporation">
//     Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//---------------------------------------------------------------------------

namespace Microsoft.Windows.Controls.Ribbon.Primitives
{
	#region Using directives

	using MS.Internal;
	using System;
	using System.Windows;
	using System.Windows.Controls;
	using System.Windows.Controls.Primitives;
	using System.Windows.Media;

	#endregion

	/// <summary>
	///   Implements the title panel of the Ribbon, this layouts out the Ribbon title
	///   and RibbonContextualTabGroup separators around the QAT and system buttons.
	/// </summary>
	public class RibbonTitlePanel : Panel
	{
		#region Private Properties

		/// <summary>
		///     DependencyProperty for Ribbon property.
		/// </summary>
		public static readonly DependencyProperty RibbonProperty =
			RibbonControlService.RibbonProperty.AddOwner(typeof(RibbonTitlePanel));

		/// <summary>
		///     This property is used to access visual style brushes defined on the Ribbon class.
		/// </summary>
		public Ribbon Ribbon
		{
			get { return RibbonControlService.GetRibbon(this); }
		}

		#endregion

		#region Protected Methods

		/// <summary>
		///   Measures the children of the RibbonTitlePanel.
		/// </summary>
		/// <param name="constraint">The space available for layout.</param>
		/// <returns>Returns the RibbonTitlePanel's desired size as determined by measurement of its children.</returns>
		protected override Size MeasureOverride(Size availableSize)
		{
			Size desiredSize = new Size();
			int count = Children.Count;
			if (count == 0)
			{
				return desiredSize;
			}

			double startContextualTabX = availableSize.Width;
			double endContextualTabX = 0.0;

			RibbonContextualTabGroupItemsControl groupHeaderItemsControl = Ribbon.ContextualTabGroupItemsControl;
			if (groupHeaderItemsControl != null && groupHeaderItemsControl.Visibility == Visibility.Visible)
			{
				// A dummy measure to ensure containers are generated.
				// We need to know FirstContextualTabHeader and LastContextualTabHeader
				if (groupHeaderItemsControl.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
				{
					groupHeaderItemsControl.Measure(availableSize);
				}

				// Calculate start and end positions
				RibbonContextualTabGroup firstContextualTab = groupHeaderItemsControl.FirstContextualTabHeader;
				if (firstContextualTab != null)
				{
					startContextualTabX = Math.Min(CalculateContextualTabGroupStartX(firstContextualTab), availableSize.Width);
				}

				RibbonContextualTabGroup lastContextualTab = groupHeaderItemsControl.LastContextualTabHeader;
				if (lastContextualTab != null)
				{
					endContextualTabX = Math.Min(CalculateContextualTabGroupEndX(lastContextualTab), availableSize.Width);
				}

				groupHeaderItemsControl.Measure(new Size(Math.Max(endContextualTabX - startContextualTabX, 0), availableSize.Height));
				desiredSize.Width += groupHeaderItemsControl.DesiredSize.Width;
				desiredSize.Height = Math.Max(desiredSize.Height, groupHeaderItemsControl.DesiredSize.Height);

			}

			FrameworkElement qat = Ribbon.QatTopHost as FrameworkElement;
			FrameworkElement titleHost = Ribbon.TitleHost as FrameworkElement;

			if (qat != null && titleHost != null)
			{
				double availableToQat = 0.0, availableToTitle;
				double leftSpace, rightSpace;

				// If ribbon is not collapsed and QAT is on top
				if (qat.Visibility == Visibility.Visible)
				{
					rightSpace = availableSize.Width - endContextualTabX;
					// 1) if there is no room on the right for Title
					// 2) or when there are no contextual tabs (i.e. endContextualTabX==0), then rightSpace=entire width
					//  want to ensure MinWidth for title if possible
					if ((DoubleUtil.LessThan(rightSpace, titleHost.MinWidth) ||
						DoubleUtil.GreaterThan(startContextualTabX, endContextualTabX))
						&& DoubleUtil.GreaterThan(startContextualTabX, titleHost.MinWidth))
					{
						// Leave titleHost.MinWidth at the left of ContextualTabGroupItemsControl for TitleHost
						availableToQat = startContextualTabX - titleHost.MinWidth;
					}
					else
					{
						// Give QAT space from 0 to start of ContextualTabGroupItemsControl
						availableToQat = startContextualTabX;
					}
					// The size computed could be negative because of invalid
					// transforms due to invalid arrange. Assume that the space
					// available is 0 in such cases and assume that validation of
					// transforms at a later point of time will fix the situation.
					availableToQat = Math.Max(availableToQat, 0);

					// Measure QAT
					qat.Measure(new Size(availableToQat, availableSize.Height));
					availableToQat = qat.DesiredSize.Width;

					desiredSize.Width += availableToQat;
					desiredSize.Height = Math.Max(desiredSize.Height, qat.DesiredSize.Height);
				}

				// Measure TitleHost. 
				// We try to give it titleHost.MinWidth, but it may get less than that when QAT has already reached qat.MinWidth
				endContextualTabX = Math.Max(endContextualTabX, availableToQat);
				rightSpace = availableSize.Width - endContextualTabX;
				leftSpace = startContextualTabX - availableToQat;

				// first measure to title's full size and see if it can fit entirely to the left
				titleHost.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
				if (DoubleUtil.LessThanOrClose(titleHost.DesiredSize.Width, leftSpace))
				{
					availableToTitle = leftSpace;
				}
				else
				{
					// title takes best of right or left
					availableToTitle = Math.Max(leftSpace, rightSpace);
				}
				// The size computed could be negative because of invalid
				// transforms due to invalid arrange. Assume that the space
				// available is 0 in such cases and assume that validation of
				// transforms at a later point of time will fix the situation.
				availableToTitle = Math.Max(availableToTitle, 0);

				titleHost.Measure(new Size(availableToTitle, availableSize.Height));
				desiredSize.Width += titleHost.DesiredSize.Width;
				desiredSize.Height = Math.Max(desiredSize.Height, titleHost.DesiredSize.Height);
			}

			desiredSize.Width = Math.Min(desiredSize.Width, availableSize.Width); // Prevent clipping
			return desiredSize;
		}

		/// <summary>
		///   Arranges the children of the RibbonTitlePanel.
		/// </summary>
		/// <param name="arrangeSize">The arranged size of the TitlePanel.</param>
		/// <returns>The size at which the TitlePanel was arranged.</returns>
		protected override Size ArrangeOverride(Size finalSize)
		{
			double x = 0.0;
			double width;
			double height;

			double startContextualTabX = 0;
			double endContextualTabX = 0;

			// Arrange ContextualTabHeaders
			RibbonContextualTabGroupItemsControl tabGroups = Ribbon.ContextualTabGroupItemsControl;
			if (tabGroups != null && tabGroups.Visibility == Visibility.Visible)
			{
				RibbonContextualTabGroup firstContextualTab = tabGroups.FirstContextualTabHeader;
				if (firstContextualTab != null)
				{
					startContextualTabX = Math.Min(CalculateContextualTabGroupStartX(firstContextualTab), finalSize.Width);
				}

				endContextualTabX = Math.Min(startContextualTabX + tabGroups.DesiredSize.Width, finalSize.Width);
				tabGroups.Arrange(new Rect(startContextualTabX, finalSize.Height - tabGroups.DesiredSize.Height, tabGroups.DesiredSize.Width, tabGroups.DesiredSize.Height));
			}

			// Arrange QuickAccessToolbar
			double qatDesiredWidth = 0.0;
			UIElement qat = Ribbon.QatTopHost;
			if (qat != null)
			{
				qatDesiredWidth = qat.DesiredSize.Width;
				qat.Arrange(new Rect(0, 0.0, qatDesiredWidth, qat.DesiredSize.Height));
			}

			endContextualTabX = Math.Max(endContextualTabX, qatDesiredWidth);

			// Arrange the title
			UIElement titleHost = Ribbon.TitleHost;
			if (titleHost != null)
			{
				x = 0.0;
				width = titleHost.DesiredSize.Width;
				height = titleHost.DesiredSize.Height;
				double leftSpace = startContextualTabX - qatDesiredWidth;

				// If title can fit entirely to the left, then arrange on the left
				// i.e. between QAT and CTGHeaders
				if (DoubleUtil.LessThanOrClose(width, leftSpace))
				{
					x = qatDesiredWidth;
				}
				else
				{
					// Arrange to the right of CTGHeaders.
					x = endContextualTabX;
				}

				titleHost.Arrange(new Rect(x, 0.0, width, height));
			}

			return finalSize;
		}

		#endregion

		#region Private Methods

		/// <summary>
		///   Calculates the x-position at which the given ContextualTabGroup should be placed on the title panel.
		/// </summary>
		/// <param name="group">The RibbonContextualTabGroup to position.</param>
		/// <returns>The x-offset at which the ContextualTabGroup should be positioned.</returns>
		private double CalculateContextualTabGroupStartX(RibbonContextualTabGroup groupHeader)
		{
			double result = 0.0;

			// A visible CTG can have some of its tabs Collapsed. 
			// We should start from the first visible Tab in this CTG.
			RibbonTab firstTab = groupHeader.FirstVisibleTab;
			if (firstTab != null && firstTab.RibbonTabHeader != null && Ribbon != null)
			{
				RibbonTabHeader tabHeader = firstTab.RibbonTabHeader;
				GeneralTransform transformRibbonTabToRibbon = tabHeader.TransformToAncestor(Ribbon);
				GeneralTransform transformRibbonToRibbonTitlePanel = Ribbon.TransformToDescendant(this);
				Point point = new Point();
				point = transformRibbonTabToRibbon.Transform(point);
				point = transformRibbonToRibbonTitlePanel.Transform(point);
				result = point.X - tabHeader.Margin.Left; // Reduce RibbonTab Margin
			}

			return result;
		}

		private double CalculateContextualTabGroupEndX(RibbonContextualTabGroup groupHeader)
		{
			double endX = CalculateContextualTabGroupStartX(groupHeader);

			foreach (RibbonTab tab in groupHeader.Tabs)
			{
				if (tab.Visibility == Visibility.Visible && tab.RibbonTabHeader != null)
				{
					endX += tab.RibbonTabHeader.DesiredSize.Width;
				}
			}
			return endX;
		}

		#endregion
	}
}